ποΈGitΠ―ΡΠ°ποΈ
specs/006-kmp-project-structure/research.md 212bc596e37fd45e75b830e36cd825972f12cf79 (212bc596) Text, 11.59 KB
Research: KMP Recommended Project Structure Alignment
R1: T383838android {} vs T383838androidLibrary {} inside T383838kotlin {}
Decision: Use T383838androidLibrary {} as the canonical DSL accessor for the KMP Android target.
Rationale: The T383838androidLibrary {} accessor is the documented API in the KMP recommended structure guide and the KMP-App-Template. While T383838android {} inside T383838kotlin {} is a transitional alias that still works (used by kotlinconf-app), T383838androidLibrary {} is the forward-looking name that clearly distinguishes between:
β’ T383838kotlin { androidLibrary {} } β KMP module Android target (new plugin)
β’ T383838android {} β top-level Android extension (legacy T383838com.android.library or T383838com.android.application)
Using T383838androidLibrary {} eliminates ambiguity and signals the project uses the recommended patterns.
Alternatives considered:
β’ Keep T383838android {} inside T383838kotlin {} (works but ambiguous, transitional)
β’ Use programmatic API only in convention plugins (already done for compileSdk/minSdk; per-module overrides still need DSL)
R2: What properties belong in T383838kotlin.androidLibrary {} vs convention plugin
Decision: Convention plugin (T383838configureKotlinMultiplatform()) handles:
β’ T383838compileSdk β
(already done)
β’ T383838minSdk β
(already done, default from config.properties)
β’ T383838namespace auto-derivation β
(already done when null)
β’ T383838androidResources.enable = false as default β NEW (absorb into convention)
Module T383838build.gradle.kts handles only overrides:
β’ T383838namespace when it differs from auto-derived (e.g., T383838feature.wifiprovision vs T383838feature.wifi-provision)
β’ T383838minSdk override (only T383838core:proto with minSdk 21)
β’ T383838androidResources { enable = true; resourcePrefix = "..." } (only T383838core:resources)
β’ T383838withHostTest {} / T383838withDeviceTest {} (per-module opt-in)
Rationale: The convention plugin already handles compileSdk/minSdk/namespace via T383838KotlinMultiplatformAndroidLibraryTarget. Adding T383838androidResources.enable = false as the convention default eliminates the most common per-module boilerplate (23 of 27 modules set this).
Alternatives considered:
β’ Move all config to convention plugin, including withHostTest (rejected: test opt-in should be explicit per-module)
β’ Keep all config in module files (rejected: adds repetitive boilerplate across 27 modules)
R3: Convention plugin audit β what needs changing
Decision: The T383838configureKotlinMultiplatform() function in T383838KotlinAndroid.kt is already well-structured. One enhancement needed:
1. Add T383838androidResources.enable = false as default in the T383838pluginManager.withPlugin block alongside compileSdk/minSdk/namespace. This eliminates the most common boilerplate line across 23 modules.
The T383838KmpLibraryConventionPlugin.kt, T383838KmpFeatureConventionPlugin.kt, and T383838KmpJvmAndroidConventionPlugin.kt do NOT need changes β they compose correctly and delegate to T383838configureKotlinMultiplatform().
Rationale: The convention plugin chain is already correct:
β’ T383838KmpLibraryConventionPlugin applies T383838com.android.kotlin.multiplatform.library (correct plugin)
β’ T383838configureKotlinMultiplatform() uses T383838KotlinMultiplatformAndroidLibraryTarget API (correct API)
β’ T383838isDesktopOnly guard is properly implemented
β’ The only gap is that individual modules repeat T383838androidResources.enable = false
Alternatives considered:
β’ Rewrite convention plugins to use DSL instead of programmatic API (rejected: programmatic API via T383838KotlinMultiplatformAndroidLibraryTarget is cleaner in convention plugins and already works)
β’ Add T383838withHostTest {} to convention by default (rejected: not all modules need host tests, and some configure T383838isIncludeAndroidResources = true)
R4: Module-by-module android {} block classification
Decision: Categorize all 27 modules into migration tiers based on complexity.
Tier 1: Simple β namespace + androidResources.enable = false (6 modules)
These modules only set namespace and disable resources. After convention absorbs T383838androidResources.enable = false, these only need namespace (or nothing if auto-derived matches).
βββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββ
β Module β namespace β Auto-derivable? β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββΌββββββββββββββββββ€
β T383838core:di β T383838org.meshtastic.core.di β β
Yes β
β T383838core:nfc β T383838org.meshtastic.core.nfc β β
Yes β
β T383838core:ui β T383838org.meshtastic.core.ui β β
Yes β
β T383838core:navigation β T383838org.meshtastic.core.navigation β β
Yes β
β T383838feature:messaging β T383838org.meshtastic.feature.messaging β β
Yes β
β T383838feature:settings β T383838org.meshtastic.feature.settings β β
Yes β
βββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββ
Tier 2: Namespace + resources disabled + withHostTest (18 modules)
These add T383838withHostTest {} opt-in. The T383838androidLibrary {} block will keep withHostTest.
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β Module β withHostTest config β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β T383838core:ble β T383838{ isIncludeAndroidResources = true } β
β T383838core:common β T383838{ isIncludeAndroidResources = true } β
β T383838core:data β T383838{ isIncludeAndroidResources = true } β
β T383838core:domain β T383838{ isIncludeAndroidResources = true } β
β T383838core:model β T383838{ isIncludeAndroidResources = true } β
β T383838core:network β T383838{ isIncludeAndroidResources = true } β
β T383838core:service β T383838{ isIncludeAndroidResources = true } β
β T383838core:takserver β T383838{ isIncludeAndroidResources = true } β
β T383838feature:connections β T383838{ isIncludeAndroidResources = true } β
β T383838feature:firmware β T383838{ isIncludeAndroidResources = true } β
β T383838feature:intro β T383838{ isIncludeAndroidResources = true } β
β T383838feature:map β T383838{ isIncludeAndroidResources = true } β
β T383838feature:node β T383838{ isIncludeAndroidResources = true } β
β T383838core:datastore β T383838{} (empty) β
β T383838core:prefs β T383838{} (empty) β
β T383838core:repository β T383838{} (empty) β
β T383838core:testing β T383838{} (empty) β
β T383838feature:wifi-provision β T383838{} (empty) β
ββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
Tier 3: Special cases (3 modules)
ββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Module β Special config β
ββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β T383838core:proto β T383838minSdk = 21 override (for ATAK compatibility) β
β T383838core:database β T383838namespace + T383838withHostTest + T383838withDeviceTest { instrumentationRunner } β
β T383838core:resources β T383838androidResources { enable = true; resourcePrefix = "meshtastic_" } + T383838withHostTβ¦ β
ββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Not affected (correctly Android-only)
β’ T383838core:api β T383838meshtastic.android.library, AIDL, publishing
β’ T383838core:barcode β T383838meshtastic.android.library, Android-only
β’ T383838feature:widget β T383838meshtastic.android.library, Glance widgets
β’ T383838app/ β T383838com.android.application
β’ T383838desktop/ β JVM only
β’ T383838screenshot-tests/ β Android test module
R5: DESKTOP_ONLY mode compatibility
Decision: No changes needed to DESKTOP_ONLY mode. The migration is purely a DSL rename.
Rationale:
β’ T383838isDesktopOnly guard in T383838KmpLibraryConventionPlugin skips T383838com.android.kotlin.multiplatform.library plugin application entirely
β’ T383838configureKotlinMultiplatform() creates inert placeholder T383838androidMain source set in desktop-only mode
β’ The T383838kotlin { androidLibrary {} } DSL accessor in module build files is provided by the plugin β when the plugin isn't applied (desktop-only), the accessor doesn't exist
β’ The existing T383838android {} accessor inside T383838kotlin {} works the same way β so the rename from T383838android {} to T383838androidLibrary {} has identical desktop-only behavior
Key validation: After migration, T383838DESKTOP_ONLY=true ./gradlew :desktop:packageUberJarForCurrentOS must succeed.
R6: T383838jvm() declaration in module build files
Decision: Remove redundant T383838jvm() from module build files as a cleanup during migration β the convention plugin handles it. Calling T383838jvm() twice is a no-op (Kotlin Gradle Plugin is idempotent for target declarations), so removing it is safe. This reduces boilerplate and makes convention ownership clear.
Alternatives considered: Keep T383838jvm() in all modules for explicitness (rejected: inconsistent since some modules already omit it, and convention plugins are the source of truth).
R7: Namespace auto-derivation coverage
Decision: Rely on convention plugin auto-derivation for most modules; only override where the derived name doesn't match.
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ¬βββββββββ
β Module path β Auto-derived namespace β Needed namespace β Match? β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌβββββββββ€
β T383838:core:ble β T383838org.meshtastic.core.bβ¦ β T383838org.meshtastic.core.ble β β
β
β T383838:core:common β T383838org.meshtastic.core.cβ¦ β T383838org.meshtastic.core.common β β
β
β T383838:core:data β T383838org.meshtastic.core.dβ¦ β T383838org.meshtastic.core.data β β
β
β T383838:core:database β T383838org.meshtastic.core.dβ¦ β T383838org.meshtastic.core.database β β
β
β T383838:core:datastore β T383838org.meshtastic.core.dβ¦ β T383838org.meshtastic.core.datastore β β
β
β T383838:core:di β T383838org.meshtastic.core.di β T383838org.meshtastic.core.di β β
β
β T383838:core:domain β T383838org.meshtastic.core.dβ¦ β T383838org.meshtastic.core.domain β β
β
β T383838:core:model β T383838org.meshtastic.core.mβ¦ β (none set β auto-derived) β β
β
β T383838:core:navigation β T383838org.meshtastic.core.nβ¦ β T383838org.meshtastic.core.navigation β β
β
β T383838:core:network β T383838org.meshtastic.core.nβ¦ β T383838org.meshtastic.core.network β β
β
β T383838:core:nfc β T383838org.meshtastic.core.nβ¦ β T383838org.meshtastic.core.nfc β β
β
β T383838:core:prefs β T383838org.meshtastic.core.pβ¦ β T383838org.meshtastic.core.prefs β β
β
β T383838:core:proto β T383838org.meshtastic.core.pβ¦ β (none set β auto-derived) β β
β
β T383838:core:repository β T383838org.meshtastic.core.rβ¦ β (none set β auto-derived) β β
β
β T383838:core:resources β T383838org.meshtastic.core.rβ¦ β (none set β auto-derived) β β
β
β T383838:core:service β T383838org.meshtastic.core.sβ¦ β T383838org.meshtastic.core.service β β
β
β T383838:core:takserver β T383838org.meshtastic.core.tβ¦ β T383838org.meshtastic.core.takserver β β
β
β T383838:core:testing β T383838org.meshtastic.core.tβ¦ β T383838org.meshtastic.core.testing β β
β
β T383838:core:ui β T383838org.meshtastic.core.ui β T383838org.meshtastic.core.ui β β
β
β T383838:feature:connections β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.connections β β
β
β T383838:feature:firmware β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.firmware β β
β
β T383838:feature:intro β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.intro β β
β
β T383838:feature:map β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.map β β
β
β T383838:feature:messaging β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.messaging β β
β
β T383838:feature:node β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.node β β
β
β T383838:feature:settings β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.settings β β
β
β T383838:feature:wifi-provision β T383838org.meshtastic.featurβ¦ β T383838org.meshtastic.feature.wifiprovision β β β
βββββββββββββββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ΄βββββββββ
Finding: Only T383838feature:wifi-provision has a namespace mismatch β the module path contains a hyphen which auto-derives as T383838org.meshtastic.feature.wifi.provision (with dot separator) but the module currently uses T383838org.meshtastic.feature.wifiprovision (no dot). This module MUST explicitly set T383838namespace in its T383838androidLibrary {} block.
All other modules that currently set T383838namespace explicitly match the auto-derived value and can safely rely on the convention plugin's auto-derivation. However, being explicit about namespace provides documentation value and prevents silent changes if the module is moved.
Decision: Remove explicit T383838namespace from modules where it matches auto-derivation. Keep it only for T383838feature:wifi-provision. This reduces boilerplate and delegates ownership to the convention plugin.
Served by rngit 1.5.2 - Generated in 0.09s